package org.oimwrapper.connectors.internal;

import Thor.API.Exceptions.tcAPIException;
import Thor.API.Operations.tcFormDefinitionOperationsIntf;
import Thor.API.Operations.tcFormInstanceOperationsIntf;
import Thor.API.Operations.tcObjectOperationsIntf;
import Thor.API.tcResultSet;
import com.thortech.util.logging.*;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

import org.oimwrapper.connectors.exceptions.OIMKeyNotFoundException;

public final class OIMObjectFormUtilities {

    private Logger logger;
    private tcObjectOperationsIntf objectOp;
    private tcFormInstanceOperationsIntf formInstOp;
    private tcFormDefinitionOperationsIntf formDefOp;
    //private tcDataProvider database;

    protected OIMObjectFormUtilities(Logger ClassLogger, tcObjectOperationsIntf ObjectOp, tcFormInstanceOperationsIntf FormInstOp, tcFormDefinitionOperationsIntf FormDefOp) {
        logger = ClassLogger;
        objectOp = ObjectOp;
        formInstOp = FormInstOp;
        formDefOp = FormDefOp;
        //database = Database;
    }

    protected Map getPrepopData(long ObjectKey, long ObjectInstanceKey, long RequesterKey) throws Exception {
        logger.debug("Entering requestUtilities.getPrepopData()");
        Map result = new HashMap();
        Map searchFor = new HashMap();
        searchFor.put("Objects.Key", Long.toString(ObjectKey));
        tcResultSet results = objectOp.findObjects(searchFor);
        if (results.getRowCount() == 1) {
            results.goToRow(0);
            if (results.getStringValue("Objects.Auto Prepopulate").equals("1")) {
                long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
                results = formInstOp.prepopulateObjectForm(RequesterKey, formDefKey, new HashMap());
                if (results.getRowCount() == 1) {
                    results.goToRow(0);
                    String[] columns = results.getColumnNames();
                    for (int i = 0; i < columns.length; i++) {
                        result.put(columns[i], results.getStringValue(columns[i]));
                    }
                }
            }
        }
        logger.debug("Exiting requestUtilities.getPrepopData()");
        return result;
    }

    protected long getChildFormDefKey(long ObjectInstanceKey, String ChildTable) throws Exception {
        logger.debug("Entering requestUtilities.getChildFormDefKey()");
        long result = 0L;
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        tcResultSet childFormDefs = formInstOp.getChildFormDefinition(formDefKey, formVersion);
        for (int i = 0; i < childFormDefs.getRowCount(); i++) {
            childFormDefs.goToRow(i);
            if (childFormDefs.getStringValue("Structure Utility.Table Name").equalsIgnoreCase(ChildTable)) {
                result = childFormDefs.getLongValue("Structure Utility.Child Tables.Child Key");
                break;
            }
        }
        logger.debug("Exiting requestUtilities.getChildFormDefKey()");
        return result;
    }

    public Map getObjectFormDisplayNames(long objectInstanceKey) throws Exception {
        logger.debug("Entering formUtilities.getObjectFormDisplayNames()");
        Map result = new HashMap();
        int formVersion = getObjectFormVersion(objectInstanceKey);
        long objectFormDefKey = formInstOp.getObjectFormDefinitionKey(objectInstanceKey);
        tcResultSet results = formDefOp.getFormFields(objectFormDefKey, formVersion);
        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            String field = results.getStringValue("Structure Utility.Additional Columns.Name");
            String label = results.getStringValue("Structure Utility.Additional Columns.Field Label");
            result.put(field, label);
        }
        logger.debug("Exiting formUtilities.getObjectFormDisplayNames()");
        return result;
    }
    
    public Map[] getObjectFormFieldInfo(long objectInstanceKey) throws Exception {
        logger.debug("Entering formUtilities.getObjectFormDisplayNames()");
        List result = new ArrayList();
        int formVersion = getObjectFormVersion(objectInstanceKey);
        long objectFormDefKey = formInstOp.getObjectFormDefinitionKey(objectInstanceKey);
        tcResultSet results = formDefOp.getFormFields(objectFormDefKey, formVersion);
        
        String[] colNames = null;
        if (results.getRowCount() > 0)
        {
            colNames = results.getColumnNames();
        }
        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            Map record = new HashMap();
            
            for(int c=0;c<colNames.length;c++)
            {
                String k = colNames[c];
                String v = results.getStringValue(k);
                record.put(k,v);
            }
            result.add(record);
        }
        logger.debug("Exiting formUtilities.getObjectFormDisplayNames()");
        return (Map[]) result.toArray(new HashMap[0]);
    }
    
    public List<Map> getObjectFormFieldInfo(long objectFormDefKey,int formVersion) throws Exception {
        logger.debug("Entering formUtilities.getObjectFormDisplayNames()");
        List result = new ArrayList();
        tcResultSet results = formDefOp.getFormFields(objectFormDefKey, formVersion);
        
        String[] colNames = null;
        if (results.getRowCount() > 0)
        {
            colNames = results.getColumnNames();
        }
        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            Map record = new HashMap();
            
            for(int c=0;c<colNames.length;c++)
            {
                String k = colNames[c];
                String v = results.getStringValue(k);
                record.put(k,v);
            }
            result.add(record);
        }
        logger.debug("Exiting formUtilities.getObjectFormDisplayNames()");
        return result;
    }
    

    public Map getObjectFormValues(long ObjectInstanceKey, boolean HideEncrypted, String DateFormat) throws Exception {

        logger.debug("Entering formUtilities.getObjectFormValues()");
        Map result = new HashMap();
        SimpleDateFormat dateFormat = DateFormat == null ? null : new SimpleDateFormat(DateFormat);
        Map labels = getObjectFormDisplayNames(ObjectInstanceKey);
        tcResultSet results = formInstOp.getObjectFormData(ObjectInstanceKey);
        if (results.getRowCount() == 1) {
            results.goToRow(0);
            String[] columns = results.getColumnNames();
            for (int i = 0; i < columns.length; i++) {
                if (!labels.containsKey(columns[i])) {
                    continue;
                }
                if (HideEncrypted && isEncryptedField(ObjectInstanceKey, columns[i])) {
                    result.put(columns[i], "********");
                } else if (isDateField(ObjectInstanceKey, columns[i])) {
                    java.util.Date value = results.getDate(columns[i]);
                    if (value.getTime() == 0L) {
                        result.put(columns[i], "");
                    } else if (DateFormat == null) {
                        result.put(columns[i], (new Timestamp(value.getTime())).toString());
                    } else {
                        result.put(columns[i], dateFormat.format(results.getDate(columns[i])));
                    }
                } else {
                    result.put(columns[i], results.getStringValue(columns[i]));
                }
            }
        } else {
            throw new OIMKeyNotFoundException("object instance", ObjectInstanceKey);
        }

        logger.debug("Exiting formUtilities.getObjectFormValues()");
        return result;

    }

    public Map[] getObjectFormChildValues(long ObjectInstanceKey, String ChildTable, boolean HideEncrypted, String DateFormat) throws Exception {

        logger.debug("Entering formUtilities.getObjectFormChildValues()");
        List result = new ArrayList();
        SimpleDateFormat dateFormat = DateFormat == null ? null : new SimpleDateFormat(DateFormat);
        Map labels = getObjectChildFormDisplayNames(ObjectInstanceKey, ChildTable);
        long childDefKey = getChildFormDefKey(ObjectInstanceKey, ChildTable);
        tcResultSet results = formInstOp.getObjectFormChildData(childDefKey, ObjectInstanceKey);
        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            Map childRecord = new HashMap();
            childRecord.put(ChildTable + "_KEY", results.getStringValue(ChildTable + "_KEY"));
            String[] columns = results.getColumnNames();
            for (int j = 0; j < columns.length; j++) {
                if (!labels.containsKey(columns[j])) {
                    continue;
                }
                if (HideEncrypted && isEncryptedField(ObjectInstanceKey, columns[j])) {
                    childRecord.put(columns[j], "********");
                } else if (isDateField(ObjectInstanceKey, columns[j])) {
                    java.util.Date value = results.getDate(columns[j]);
                    if (value.getTime() == 0L) {
                        childRecord.put(columns[j], "");
                    } else if (DateFormat == null) {
                        childRecord.put(columns[j], (new Timestamp(value.getTime())).toString());
                    } else {
                        childRecord.put(columns[j], dateFormat.format(results.getDate(columns[j])));
                    }
                } else {
                    childRecord.put(columns[j], results.getStringValue(columns[j]));
                }
            }
            result.add(childRecord);
        }

        logger.debug("Exiting formUtilities.getObjectFormChildValues()");
        return (Map[]) result.toArray(new HashMap[0]);

    }

    public boolean isEncryptedField(long ObjectInstanceKey, String Field) throws Exception {
        logger.debug("Entering formUtilities.isEncryptedField()");
        boolean result = false;
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        tcResultSet fields = formDefOp.getFormFields(formDefKey, formVersion);
        for (int i = 0; i < fields.getRowCount(); i++) {
            fields.goToRow(i);
            String fieldName = fields.getStringValue("Structure Utility.Additional Columns.Name");
            if (fieldName.equalsIgnoreCase(Field)) {
                String fieldEncrypted = fields.getStringValue("Structure Utility.Additional Columns.Encrypted");
                result = fieldEncrypted.equalsIgnoreCase("1");
                break;
            }
        }
        logger.debug("Exiting formUtilities.isEncryptedField()");
        return result;
    }

    public boolean isDateField(long ObjectInstanceKey, String Field) throws Exception {

        logger.debug("Entering formUtilities.isDateField()");
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        boolean result = isFieldType(Field, "DateFieldDlg", formVersion, formDefKey);
        logger.debug("Exiting formUtilities.isITResourceField()");
        return result;
    }

    public boolean isFieldType(String Field, String Type, int FormVersion, long FormDefKey) throws Exception {

        logger.debug("Entering formUtilities.isFieldType()");
        boolean result = false;
        tcResultSet fields = formDefOp.getFormFields(FormDefKey, FormVersion);
        for (int i = 0; i < fields.getRowCount(); i++) {
            fields.goToRow(i);
            String fieldName = fields.getStringValue("Structure Utility.Additional Columns.Name");
            if (fieldName.equalsIgnoreCase(Field)) {
                String fieldType = fields.getStringValue("Structure Utility.Additional Columns.Field Type");
                result = fieldType.equalsIgnoreCase(Type);
                break;
            }
        }
        logger.debug("Exiting formUtilities.isFieldType()");
        return result;
    }

    public Map getObjectChildFormDisplayNames(long ObjectInstanceKey, String ChildTable) throws Exception {

        logger.debug("Entering formUtilities.getObjectFormDisplayNames()");
        Map result = new HashMap();
        int childFormVersion = getChildFormVersion(ObjectInstanceKey, ChildTable);

        long childFormDefKey = getChildFormDefKey(ObjectInstanceKey, ChildTable);
        tcResultSet results = formDefOp.getFormFields(childFormDefKey, childFormVersion);
        for (int i = 0; i < results.getRowCount(); i++) {
            results.goToRow(i);
            String field = results.getStringValue("Structure Utility.Additional Columns.Name");
            String label = results.getStringValue("Structure Utility.Additional Columns.Field Label");
            result.put(field, label);
        }
        logger.debug("Exiting formUtilities.getObjectFormDisplayNames()");
        return result;
    }
    
    
    public Map<String,List<Map>> getObjectChildFormFieldInfo(long formDefKey, int formVersion) throws Exception
    {
        
        Map<String,List<Map>> childFormInfoMap = new HashMap<String,List<Map>>();
        
        try {
            tcResultSet childFormDefs = formInstOp.getChildFormDefinition(formDefKey, formVersion);
            
            for(int i=0;i<childFormDefs.getRowCount();i++)
            {
                childFormDefs.goToRow(i);
                String tableName = childFormDefs.getStringValue("Structure Utility.Table Name");
                int childVersion = childFormDefs.getIntValue("Structure Utility.Child Tables.Child Version");
                long childDefKey = childFormDefs.getLongValue("Structure Utility.Child Tables.Child Key");
                tcResultSet fields = formDefOp.getFormFields(childDefKey, childVersion);
                
                String[] colNames = null;
                List fieldList = null;
                if (fields.getRowCount() > 0)
                {
                    colNames = fields.getColumnNames();
                    fieldList = new ArrayList();
                }
                for(int f=0;f<fields.getRowCount();f++)
                {
                    
                    fields.goToRow(f);
                    Map record = new HashMap();
                    for(String col : colNames)
                    {
                        String k = col;
                        String v = fields.getStringValue(k);
                        record.put(k,v);
                    }
                    fieldList.add(record);
                }
                childFormInfoMap.put(tableName,fieldList);
            }
            return childFormInfoMap;
        } 
        catch (Exception e) 
        {
            logger.error("APIError",e);
            throw e;
        }
    }
    
    public int getChildFormVersion(long ObjectInstanceKey, String ChildTable) throws Exception {

        logger.debug("Entering formUtilities.getChildFormDefKey()");
        int result = 0;
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        tcResultSet childFormDefs = formInstOp.getChildFormDefinition(formDefKey, formVersion);
        for (int i = 0; i < childFormDefs.getRowCount(); i++) {
            childFormDefs.goToRow(i);
            if (childFormDefs.getStringValue("Structure Utility.Table Name").equalsIgnoreCase(ChildTable)) {
                result = childFormDefs.getIntValue("Structure Utility.Child Tables.Child Version");
                break;
            }
        }
        logger.debug("Exiting formUtilities.getChildFormDefKey()");
        return result;
    }
    
    public String[] getChildTables(long ObjectInstanceKey) throws Exception {

        logger.debug("Entering formUtilities.getChildTables()");
        int formVersion = getObjectFormVersion(ObjectInstanceKey);
        long formDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
        tcResultSet childFormDefs = formInstOp.getChildFormDefinition(formDefKey, formVersion);
        String[] result = new String[childFormDefs.getRowCount()];
        for (int i = 0; i < childFormDefs.getRowCount(); i++) {
            childFormDefs.goToRow(i);
            result[i] = childFormDefs.getStringValue("Structure Utility.Table Name");
        }
        logger.debug("Exiting formUtilities.getChildTables()");
        return result;
    }
    
    public void setObjectFormValues(long ObjectInstanceKey, Map Data) throws Exception {

        logger.debug("Entering formUtilities.setObjectFormValues()");
        formInstOp.setObjectFormData(ObjectInstanceKey, Data);
        logger.debug("Exiting formUtilities.setObjectFormValues()");

    }
    
    public void updateObjectFormChildValue(long ObjectInstanceKey, String ChildTable, long ChildDataKey, Map Data) throws Exception {

        logger.debug("Entering formUtilities.updateObjectFormChildValue()");
        long childDefKey = getChildFormDefKey(ObjectInstanceKey, ChildTable);
        formInstOp.updateObjectFormChildData(childDefKey, ChildDataKey, Data);
        logger.debug("Exiting formUtilities.updateObjectFormChildValue()");

    }
    
    public void addObjectFormChildValue(long ObjectInstanceKey, String ChildTable, Map Data) throws Exception {

        logger.debug("Entering formUtilities.addObjectFormChildValue()");
        long childDefKey = getChildFormDefKey(ObjectInstanceKey, ChildTable);
        formInstOp.addObjectFormChildData(childDefKey, ObjectInstanceKey, Data);
        logger.debug("Exiting formUtilities.addObjectFormChildValue()");

    }
    
    public void setObjectFormValue(long ObjectInstanceKey, String Attribute, java.util.Date Value) throws Exception {

        logger.debug("Entering formUtilities.setObjectFormValue()");
        Map data = new HashMap();
        Timestamp timestamp = new Timestamp(Value.getTime());
        data.put(Attribute, timestamp.toString());
        setObjectFormValues(ObjectInstanceKey, data);
        logger.debug("Exiting formUtilities.setObjectFormValue()");

    }
    
    public void removeObjectDataChildValue(long ObjectInstanceKey, String ChildTable, long ChildDataKey) throws Exception {

        logger.debug("Entering formUtilities.removeObjectDataChildValue()");
        long childDefKey = getChildFormDefKey(ObjectInstanceKey, ChildTable);
        formInstOp.removeObjectFormChildData(childDefKey, ChildDataKey);
        logger.debug("Exiting formUtilities.removeObjectDataChildValue()");
    }
    
    private int getObjectFormVersion(long ObjectInstanceKey) throws Exception
    {
        logger.debug("Entering formUtilities.getObjectFormVersion()");
        int formVersion = 0;
        try
        {
            formVersion = formInstOp.getObjectFormVersion(ObjectInstanceKey);
            return formVersion;
        }
        catch(tcAPIException e)
        {
            logger.error("API Failed:getObjectFormVersion",e);
            throw e;
        }
        catch(Exception e)
        {
            logger.info("Failed to find Object Version. Trying Slow Approach:" + e.getMessage());
        }
        
        try
        {
            long objectFormDefKey = formInstOp.getObjectFormDefinitionKey(ObjectInstanceKey);
            formVersion = formInstOp.getActiveVersion(objectFormDefKey);
            return formVersion;
        }
        catch(tcAPIException e)
        {
            logger.error("API Failed:getObjectFormVersion slow",e);
            throw e;
        }
        catch(Exception e)
        {
            logger.info("Failed to find Object Version. With Slow Approach:" + e.getMessage());
        }
        throw new Exception("Failed to find Object Version:" + ObjectInstanceKey);
    }
    
    public int getObjectFormVersion(String tableName) throws Exception
    {
        try
        {
            Map filter = new HashMap();
            filter.put("Structure Utility.Table Name",tableName);
            tcResultSet rs = formDefOp.findForms(filter);
            for(int i=0;i<rs.getRowCount();i++)
            {
                rs.goToRow(i);
                String name = rs.getStringValue("Structure Utility.Table Name");
                int ver = rs.getIntValue("Structure Utility.Active Version");
                return ver;
            }
            throw new Exception("TableName not Found:" + tableName);
        }
        catch(Exception e)
        {
            logger.error("APIERROR",e);
            throw e;
        }
    }
    
    public long getObjectFormDefinitionKey(String tableName) throws Exception
    {
        try
        {
            Map filter = new HashMap();
            filter.put("Structure Utility.Table Name",tableName);
            tcResultSet rs = formDefOp.findForms(filter);
            for(int i=0;i<rs.getRowCount();i++)
            {
                rs.goToRow(i);
                String name = rs.getStringValue("Structure Utility.Table Name");
                long key = rs.getLongValue("Structure Utility.Key");
                return key;
            }
            throw new Exception("TableName not Found:" + tableName);
        }
        catch(Exception e)
        {
            logger.error("APIERROR",e);
            throw e;
        }
    }
    
    
}
